#include <reg51.h>
#include <stdio.h>
#include <intrins.h>
#define CharLen 6

//数据位定义

sbit DQ = P3 ^ 7;
sbit SIGN = P1 ^ 7;
sbit E = P2 ^ 2;     // LCD使能端口定义
sbit RW = P2 ^ 1;    // LCD读写使能端口定义
sbit RS = P2 ^ 0;    // LCD数据/命令选择方式端口定义
sbit bflag = P1 ^ 7; // LCD忙指示端口，高电平为忙

//结构体定义

struct temperature {
    unsigned char high, low;
};

//时延函数定义

void delay_10us(unsigned char n) {  // 12MHz 晶振 对应的 10 微秒级时延
    while (n--);
}

void delay_ms(unsigned int n) {     //延时n毫秒
    unsigned int i = 0, j = 0;
    for(i = 0; i < n; i++)
        for(j = 0; j < 123; j++);
}

void getControl_1602() {      //获取 LCD 使用权
        do {
        P1 = 0xff;
        RS = 0;         // RS = 0，RW = 1 时才可读忙信号
        RW = 1;
        E = 0;
        _nop_();        //读取 P2 口数据，读取 bflag
        E = 1;
    } while (bflag);    //当其值为 0 时，表示不忙，才可以接收命令或者数据信号
}

void wreg_1602(unsigned char com) {     //函数功能：写指令函数 
    getControl_1602();
    RS = 0;         //当RS = 0，RW = 0时，表明写入的是命令
    RW = 0;
    E = 1;
    P1 = com;       //当使能由高到低时，LCD 执行相应命令
    E = 0;
}

void wchar_1602(unsigned char dat) {    //函数功能：写数据函数
    getControl_1602();
    RS = 1;             //当 RS = 1，RW = 0 时，表明写入的是数据
    RW = 0;
    E = 1;
    P1 = dat;           //当使能由高到低时，LCD 执行相应命令
    E = 0;
}

void wstr_1602(unsigned char str[]) {       // 写字符串
    int i = 0;
    while (str[i] != '\0') {
        wchar_1602(str[i]);
        i += 1;
    }

}

void lcd_pos(unsigned char pos) {       //函数功能：指定数据显示地址
    wreg_1602(pos | 0x80);      //指令8，最高位必须为1，第一行最左边地址为0x00；第二行为0x40 
}

void init_1602() {//函数功能:设置LCD_1602的开显示 光标不闪烁等的功能
    wreg_1602(0x38); //指令6，8位数据总线，双行显示，每位采用5*7点阵
    wreg_1602(0x08); //指令4，关显示，关光标，无闪烁
    wreg_1602(0x06); //指令3，光标自动右移，文字不移动
    wreg_1602(0x01); //指令1，清显示
    wreg_1602(0x0c); //指令4，开显示

    lcd_pos(0x00);
    wstr_1602("System starting");
    lcd_pos(0x40 + 0x02);
    wstr_1602("Please wait.");
    delay_ms(750);  //时延 750ms 跳过 1602 的启动延时
    wreg_1602(0x01);
    wreg_1602(0x0c);
}



// 温度传感器部分

int DS18B20_init() { // 向温度传感器发送初始化信号
    int i = 0;
    DQ = 1;
    delay_10us(1);
    DQ = 0;
    delay_10us(80);
    DQ = 1;
    while (DQ) {
        delay_10us(100);
        i++;
        if (i > 5) {
            return 0;
        }
    }
    return 1;
}

void write_byte(unsigned char dat) {
    unsigned char i = 0;

    for (i = 0; i < 8; i++) {
        DQ = 0;
        _nop_();
        DQ = dat & 0x01;
        delay_10us(10);
        DQ = 1;
        dat = dat >> 1;
    }
    delay_10us(10);
}

unsigned char read_byte() {
    unsigned char i, j, dat = 0;
    for (i = 0; i < 8; i++) {
        DQ = 0;
        _nop_();
        DQ = 1;
        _nop_();
        j = DQ;
        delay_10us(10);
        DQ = 1;
        _nop_();
        dat = (dat >> 1) | (j << 7);
    }
    return dat;
}

struct temperature read_temper() {
    struct temperature t;
    DS18B20_init();
    delay_10us(15);
    write_byte(0xcc);
    write_byte(0x44);
    DS18B20_init();
    delay_10us(15);
    write_byte(0xcc);
    write_byte(0xbe);
    t.low = read_byte();
    t.high = read_byte();
    return t;
}

/* struct temperature get_temperature(struct temperature temper) {
    struct temperature tmp;
    tmp = read_temper();
    if (temper.high == tmp.high && temper.low == tmp.low)
        return temper;
    else
        return tmp;
} */

void output_temperature(struct temperature tmp) {
    //float tp;
    unsigned int t, tm, tp;
    unsigned char tc[CharLen];

    lcd_pos(0x00 + 0x02); //第一行
    wstr_1602("Temperature");
    lcd_pos(0x40 + 0x04); //第二行

    t = tmp.high;
    t = (t << 8) | tmp.low;
    if (t < 0x8000)
        wchar_1602(' ');
    else {
        t = t - 1;
        t = ~ t;
        wchar_1602('-');
    }
    
    tm = (t >> 4) / 100;
    tp = (t >> 4) / 10 % 10;
    tc[0] = tm? tm + '0': ' ';
    tc[1] = (tm == 0 && tp == 0)? ' ': tp + '0';
    tc[2] = (t >> 4) % 10 + '0';
    tc[3] = '.';
    tc[4] = (t >> 3) %2 * 5 + '0';
    tc[5] = '\0';
    
    wstr_1602(tc);
}

int main() {
    struct temperature t;
    t = read_temper();  //调用读温度初始化温度传感器
    init_1602();
    while (1) {
        if (t == read_temper())
            continue;   //温度相同则不会重复覆盖显示
        output_temperature(read_temper());
    }
}
